
/**
 * @param  {} {url
 * @param  {} pingTimeout=15000       每15秒发送一次心跳。如果接收到任何后端消息，计时器将重置
 * @param  {} pongTimeout=10000       发送Ping消息后，如果没有收到后端消息,连接将在10秒内断开
 * @param  {} reconnectTimeout=2000   重新连接的间隔
 * @param  {} pingMsg='heartbeat'     ping心跳值
 * @param  {} pingMsgData='received param'     ping心跳值
 * @param  {} pingMsgProperty='received properties'     ping心跳值
 * @param  {} repeatLimit=null        重新连接的尝试次数    null无限制
 * }
 */
import UnZip from '@/assets/js/UnZip';
import Zip from "../../assets/js/Zip";

function WebsocketComm({
                         url,
                         pingTimeout = 15000,
                         pongTimeout = 10000,
                         reconnectTimeout = 2000,
                         pingMsg = 'heartbeat',
                         repeatLimit = null
                       }) {
  this.opts = {
    url: url,
    pingTimeout: pingTimeout,
    pongTimeout: pongTimeout,
    reconnectTimeout: reconnectTimeout,
    pingMsg: pingMsg,
    repeatLimit: repeatLimit
  }
  this.ws = null
  this.repeat = 0
  this.onclose = () => {
  }
  this.onerror = () => {
  }
  this.onopen = () => {
  }
  this.onmessage = () => {
  }
  this.onreconnect = () => {
  }

  this.createWebSocket()
}

WebsocketComm.prototype = {
  createWebSocket: function () {
    this.forbidReconnect = false
    try {
      this.ws = new WebSocket(this.opts.url)
      this.initEventHandle()
    } catch (e) {
      this.reconnect()
      throw e;
    }
  },
  initEventHandle: function () {
    this.ws.onclose = () => {
      console.log('websocket onclose')
      this.onclose()
      this.reconnect()
    }
    this.ws.onerror = () => {
      console.log('websocket onerror')
      this.onerror()
      this.reconnect()
    }
    this.ws.onopen = () => {
      console.log('websocket onopen')
      this.repeat = 0;
      this.onopen()
      //心跳检测重置
      this.heartCheck()
    }
    this.ws.onmessage = (event) => {
      let data = null
      //解压缩
      if (JSON.parse(event.data).zip) {
        data = UnZip(JSON.parse(event.data).data)
      } else {
        data = JSON.parse(event.data).data
      }
      if (data === this.opts.pingMsg || data === 'received param' || data === 'received properties') {
        //this.heartCheck()
        return;
      }
      this.onmessage(data)
      //如果获取到消息，心跳检测重置
      //拿到任何消息都说明当前连接是正常的
      //this.heartCheck()
    }
  },
  reconnect: function () {

    if (this.opts.repeatLimit > 0 && this.opts.repeatLimit <= this.repeat) return
    if (this.lockReconnect || this.forbidReconnect) return
    console.log('websocket reconnect')

    this.lockReconnect = true
    this.repeat++         //必须在lockReconnect之后，避免进行无效计数

    this.onreconnect()    //重连回调

    //没连接上会一直重连，设置延迟避免请求过多
    setTimeout(() => {
      this.createWebSocket()
      this.lockReconnect = false
    }, this.opts.reconnectTimeout)
  },
  send: function (msg) {
    if(msg=== this.opts.pingMsg){
      this.ws.send(msg)
    }
    else if(msg === 'displayConfig'){//异常配置修改后，传参，目的是为了告诉后端配置修改，有些不需要提醒的异常不用往前端推送
      this.ws.send(msg)
    }
    else {
      if(msg.length > 5000) {
        let dataStr = Zip(msg);
        this.ws.send(JSON.stringify({"paramData": dataStr, zip: true}))
      }
      else {
        this.ws.send(JSON.stringify({"paramData": msg, zip: false}))
      }
    }
  },
  //心跳检测
  heartCheck: function () {
    this.heartReset()
    this.heartStart()
  },

  heartStart: function () {
    //不再重连就不再执行心跳
    if (this.forbidReconnect) return
    this.pingTimeoutId = setTimeout(() => {
      //这里发送一个心跳，后端收到后，返回一个心跳消息，
      //onmessage拿到返回的心跳就说明连接正常
      this.send(this.opts.pingMsg)
      //如果超过一定时间还没重置，说明后端主动断开了
      this.pongTimeoutId = setTimeout(() => {
        //如果onclose会执行reconnect，我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
        this.ws.close()
      }, this.opts.pongTimeout)
      this.heartCheck();
    }, this.opts.pingTimeout)
  },

  heartReset: function () {
    if (this.pingTimeoutId) {
      clearTimeout(this.pingTimeoutId)
    }
    if (this.pongTimeoutId) {
      clearTimeout(this.pongTimeoutId)
    }
  },
  close: function () {
    //如果手动关闭连接,说明不再重连
    this.forbidReconnect = true
    this.heartReset()
    this.ws.close()
  }
}

export default WebsocketComm;
